﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MathNet.Numerics;
using System.IO;
using DiffusionAnalyticalSolution;
using MathNet.Numerics.Distributions;

namespace DegredationAnalyticalSol
{
    class Program
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="communication"></param>
        /// <param name="analytic"></param>
        /// <param name="Ts">Second</param>
        /// <param name="NumberOfSentMolecules"></param>
        /// <param name="maxNumberOfSymbolDurationsToLookBack"></param>
        static void CalculateFracs(Communication communication, Analytic analytic, double Ts, double NumberOfSentMolecules, int maxNumberOfSymbolDurationsToLookBack)
        {
            for (int i = 1; i < communication._bitValues.Length; i++)
            {
                int j;
                if (maxNumberOfSymbolDurationsToLookBack != 0)
                {
                    j = i - maxNumberOfSymbolDurationsToLookBack;

                    if (j < 0)
                    {
                        j = 0;
                    }
                }
                else
                {
                    j = 0;
                }

                double frac = 0;

                for (; j <= i - 1; j++)
                {
                    if (communication._bitValues[j] != 0)
                    {
                        frac += analytic.DifferenceOfMoleculesWithDecompositionConsidered((i - j - 1) * Ts, (i - j) * Ts);
                    }
                }

                communication._fracValues[i - 1] = frac;

                if (i % 1000 == 0)
                {
                    Console.WriteLine(i);
                    Console.WriteLine("Bu arada hash'e vurma sayısı " + Analytic._numberOfTimesHitToHash);
                    Analytic._numberOfTimesHitToHash = 0;
                }
            }
        }

        static double CalculatePC0andPC1Gaussian(Communication communication, double tau, double NumberOfSentMolecules, out double pc1)
        {
            var pc0 = 0.0;
            pc1 = 0.0;
            var count0 = 0;
            var count1 = 0;

            for (int i = 0; i < communication._bitValues.Length - 1; i++)
            {
                if (communication._bitValues[i] == 0)
                {
                    var normal = new Normal(NumberOfSentMolecules * communication._fracValues[i], Math.Sqrt(NumberOfSentMolecules * communication._fracValues[i] * (1 - communication._fracValues[i])));

                    count0++;
                    pc0 += normal.CumulativeDistribution(tau);
                }
                else
                {
                    var normal = new Normal(NumberOfSentMolecules * communication._fracValues[i], Math.Sqrt(NumberOfSentMolecules * communication._fracValues[i] * (1 - communication._fracValues[i])));

                    count1++;
                    pc1 += (1 - normal.CumulativeDistribution(tau));
                }
            }

            pc1 = pc1 / count1;

            return pc0 / count0;
        }

        static double CalculatePC0andPC1Poisson(Communication communication, double tau, double NumberOfSentMolecules, out double pc1)
        {
            var pc0 = 0.0;
            pc1 = 0.0;
            var count0 = 0;
            var count1 = 0;

            for (int i = 0; i < communication._bitValues.Length - 1; i++)
            {
                var n = NumberOfSentMolecules * communication._fracValues[i];
                if (n <= 0.0)
                {
                    n = 0.0000000001;
                }

                if (communication._bitValues[i] == 0)
                {
                    var poisson = new Poisson(n);

                    count0++;
                    pc0 += poisson.CumulativeDistribution(tau);
                }
                else
                {
                    var poisson = new Poisson(n);

                    count1++;
                    pc1 += (1 - poisson.CumulativeDistribution(tau));
                }
            }

            pc1 = pc1 / count1;

            return pc0 / count0;
        }

        static double CalculatePC0andPC1NONOISE(Communication communication, double tau, out double pc1)
        {
            var pc0 = 0.0;
            pc1 = 0.0;
            var count0Correct = 0;
            var count0Total = 0;
            var count1Correct = 0;
            var count1Total = 0;

            for (int i = 0; i < communication._bitValues.Length - 1; i++)
            {
                if (communication._bitValues[i] == 0)
                {
                    if (communication._fracValues[i] < tau)
                    {
                        count0Correct++;
                    }

                    count0Total++;
                }
                else
                {
                    if (communication._fracValues[i] >= tau)
                    {
                        count1Correct++;
                    }

                    count1Total++;
                }
            }

            pc1 = count1Correct / count1Total;

            return count0Correct / count0Total;
        }

        static void Main(string[] args)
        {
            OptimizeTsRuns();
        }

        static void RegularSoln()
        {

            var p = new Poisson(5);

            var cdf0 = p.CumulativeDistribution(0);

            
            var distance = 18;
            var symbolDuration = 0.512;
            var duration = 10000; //bits
            var numberOfMolecules = 1000;
            var lookbackBits = 0;
            var halfLife = 1.024;
            var lambda = Math.Log(2) / halfLife;

            var communication = new Communication(duration);

            communication.InitializeRandom();

            var analytic = new Analytic(10, distance, 79.4, lambda);

            CalculateFracs(communication, analytic, symbolDuration, numberOfMolecules, lookbackBits);

            StreamWriter sw = new StreamWriter(String.Format("Logs\\Analitik Gaussian D={0}, N={1}, HL={2}, T_s={3}, Duration = {4}, Lookback = {5}.txt", distance,
               numberOfMolecules, halfLife, symbolDuration, duration, lookbackBits), false);

            //StreamWriter sw = new StreamWriter(String.Format("Logs\\asdasdasd.txt", distance, 
            //    numberOfMolecules, halfLife, symbolDuration, duration, lookbackBits), false);

            var maxI = 0.0;

            for (int i = 0; i < numberOfMolecules; i++)
            {
                try
                {
                    double pc1;
                    var pc0 = CalculatePC0andPC1Gaussian(communication, i, numberOfMolecules, out pc1);

                    var I = DiffisionWithDecomposition.ShannonsMutualInformation.MutualInformation(pc0, pc1);

                    if (maxI < I)
                    {
                        maxI = I;
                    }

                    sw.WriteLine(i + " " + pc1 + " " + pc0 + " " + I);

                    Console.WriteLine(i);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    Console.ReadLine();
                }

            }


            Console.WriteLine(maxI);

            sw.Flush();
            sw.Close();

            var a = 12312 % 4;
        }

        static void OptimizeTsRuns()
        {
            
            var duration = 10000; //bits
            var numberOfMolecules = 1000;
            var lookbackBits = 0;

            Parallel.For(32, 33, d =>
            {
                var distance = d;

                for (double t = 0.512; t <= 10; t *= 2)
                {
                    var halfLife = t;
                    var lambda = Math.Log(2) / halfLife;

                    StreamWriter sw01 = new StreamWriter(String.Format("Logs\\pe0pe1\\Analitik Poisson D={0}, N={1}, Dec={2}, T_s={3}, Duration = {4}, Lookback = {5}.txt", distance,
                      numberOfMolecules, halfLife, "0.01 - 0.9", duration, lookbackBits), false);

                    StreamWriter sw0 = new StreamWriter(String.Format("Logs\\pe0\\Analitik Poisson D={0}, N={1}, Dec={2}, T_s={3}, Duration = {4}, Lookback = {5}.txt", distance,
                          numberOfMolecules, halfLife, "0.01 - 0.9", duration, lookbackBits), false);

                    StreamWriter sw1 = new StreamWriter(String.Format("Logs\\pe1\\Analitik Poisson D={0}, N={1}, Dec={2}, T_s={3}, Duration = {4}, Lookback = {5}.txt", distance,
                          numberOfMolecules, halfLife, "0.01 - 0.9", duration, lookbackBits), false);


                    var analytic = new Analytic(10, distance, 79.4, lambda);

                    for (double k = 0.01; k <= 0.901; k += 0.01)
                    {
                        var symbolDuration = k;

                        var communication = new Communication(duration);

                        communication.InitializeRandom();

                        CalculateFracs(communication, analytic, symbolDuration, numberOfMolecules, lookbackBits);

                        var pc0Array = new double[numberOfMolecules];
                        var pc1Array = new double[numberOfMolecules];
                        var pc0pc1Array = new double[numberOfMolecules];

                        for (int i = 0; i < numberOfMolecules; i++)
                        {
                            double pc1;
                            var pc0 = CalculatePC0andPC1Poisson(communication, i, numberOfMolecules, out pc1);

                            pc0Array[i] = 1 - pc0;
                            pc1Array[i] = 1 - pc1;
                            pc0pc1Array[i] = 1 - pc0 + 1 - pc1;
                        }

                        var stringBuilder0 = new StringBuilder();
                        var stringBuilder1 = new StringBuilder();
                        var stringBuilder01 = new StringBuilder();

                        for (int i = 0; i < numberOfMolecules; i++)
                        {
                            stringBuilder0.Append(pc0Array[i] + " ");
                            stringBuilder1.Append(pc1Array[i] + " ");
                            stringBuilder01.Append(pc0pc1Array[i] + " ");
                        }

                        sw0.WriteLine(stringBuilder0.ToString());
                        sw1.WriteLine(stringBuilder1.ToString());
                        sw01.WriteLine(stringBuilder01.ToString());

                        sw0.Flush();
                        sw1.Flush();
                        sw01.Flush();
                    }

                    sw0.Close();
                    sw1.Close();
                    sw01.Close();


                }
            });
        
        }
            
    }
}
